// 这是一个 Vue 组件，使用了 Three.js 来加载和渲染 3D 模型
<script setup>
// 引入 Three.js 和 Vue 的相关模块
import * as THREE from 'three'
import { ref } from 'vue'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'

// 引入 Three.js 的加载器和后处理模块
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js'
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js'
import { OutlinePass } from 'three/examples/jsm/postprocessing/OutlinePass.js'
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js'
import { FXAAShader } from 'three/examples/jsm/shaders/FXAAShader'

// 定义组件的 props，接收模型的 URL
const props = defineProps({
  modelUrl: {
    type: String,
    required: true
  }
})

// 定义全局变量和暴露的函数
let baseUrl = window.config.VUE_APP_UPLOAD_URL
// 暴露给父组件的函数
defineExpose({
  init,
  initFile,
  setModelSize,
  combination,
  splitting
})

// 定义事件发射器
const emit = defineEmits(['loadModelOver', 'checkedModel', 'toggleModel'])

// 初始化函数，调用多个子初始化函数
function init() {
  initScene() // 初始化场景
  initCamera() // 初始化相机
  initLight() // 初始化灯光
  initRenderer() // 初始化渲染器
  initFile() // 加载模型文件
}

// 定义 Vue 的响应式变量
let loading = ref(false)
let modelBox = ref()

// 定义 Three.js 的核心对象
let scene
let camera
let renderer
let controls, clock

// 初始化场景
function initScene() {
  scene = new THREE.Scene()
  clock = new THREE.Clock()
}

// 初始化相机
function initCamera() {
  loading.value = true
  camera = new THREE.PerspectiveCamera(
    75, // 视角
    modelBox.value.clientWidth / modelBox.value.clientHeight, // 横纵比
    0.1, // 近裁剪面
    1000 // 远裁剪面
  )
}

// 初始化渲染器
function initRenderer() {
  renderer = new THREE.WebGLRenderer({
    alpha: true, // 透明背景
    antialias: true, // 抗锯齿
    preserveDrawingBuffer: true // 保留绘图缓冲区
  })
  renderer.setSize(modelBox.value.clientWidth, modelBox.value.clientHeight)
  modelBox.value.appendChild(renderer.domElement)

  renderer.render(scene, camera)

  createRenderChannel() // 创建渲染通道

  // 添加鼠标事件监听器
  renderer.domElement.addEventListener('dblclick', onDoubleClick)
  renderer.domElement.addEventListener('mousedown', onMouseDown)
  renderer.domElement.addEventListener('mousemove', onMouseMove)
  renderer.domElement.addEventListener('mouseup', onMouseUp)
}

// 创建渲染通道，添加后处理效果
let composer, outlinePass
function createRenderChannel() {
  composer = new EffectComposer(renderer)
  composer.setSize(modelBox.value.clientWidth, modelBox.value.clientHeight)

  const renderPass = new RenderPass(scene, camera)
  composer.addPass(renderPass)

  outlinePass = new OutlinePass(
    new THREE.Vector2(modelBox.value.clientWidth, modelBox.value.clientHeight),
    scene,
    camera
  )
  outlinePass.pulsePeriod = 2 // 律动效果
  outlinePass.renderToScreen = true
  outlinePass.edgeGlow = 1 // 边缘微光强度
  outlinePass.edgeStrength = 3
  outlinePass.edgeThickness = 2
  outlinePass.visibleEdgeColor.set('#ff0000') // 可见边缘颜色
  outlinePass.hiddenEdgeColor.set('#ff0000') // 隐藏边缘颜色
  outlinePass.selectedObjects = []
  composer.addPass(outlinePass)

  // 添加抗锯齿效果
  let fxaaPass = new ShaderPass(FXAAShader)
  fxaaPass.uniforms['resolution'].value.set(
    1 / modelBox.value.clientWidth,
    1 / modelBox.value.clientHeight
  )
  composer.addPass(fxaaPass)
}

// 初始化灯光
function initLight() {
  const light = new THREE.AmbientLight(0xffffff, 0.8) // 环境光
  scene.add(light)
}

// 动画循环
function animate() {
  if (composer) {
    composer.render()
  }
  if (resizeRendererToDisplaySize(renderer)) {
    const canvas = renderer.domElement
    camera.aspect = canvas.clientWidth / canvas.clientHeight
    camera.updateProjectionMatrix()
  }
  var deltaTime = clock.getDelta()
  if (mixer) {
    mixer.update(deltaTime) // 更新动画混合器
  }
  requestAnimationFrame(animate)
}

// 调整渲染器大小
function resizeRendererToDisplaySize(renderer) {
  const canvas = renderer.domElement
  const width = canvas.clientWidth
  const height = canvas.clientHeight
  const needResize = canvas.width !== width || canvas.height !== height
  if (needResize) {
    renderer.setSize(width, height, false)
  }
  return needResize
}

// 初始化控制器
function initControls(x, y, z) {
  controls = new OrbitControls(camera, renderer.domElement)
  controls.target.set(x, y, z)
  controls.enablePan = false // 禁用摄像机平移
  controls.mouseButtons = {
    LEFT: THREE.MOUSE.PAN,
    MIDDLE: THREE.MOUSE.DOLLY,
    RIGHT: THREE.MOUSE.ROTATE
  }
  controls.update()
  emit('loadModelOver') // 通知父组件模型加载完成
}

// 加载模型
let model, animations, url, modelSize
function initFile() {
  if (url === props.modelUrl) return false // 防止重复加载
  var gltf_loader = new GLTFLoader()
  gltf_loader.load(
    baseUrl + props.modelUrl,
    function (glat) {
      let newModel
      url = props.modelUrl
      if (model && newModel === model) return false
      if (model) {
        newModel = glat.scene
        scene.remove(model) // 从场景中移除旧模型
        disposeModel(model) // 清理旧模型的资源
      }
      model = glat.scene
      const textureCube = new THREE.TextureLoader().load(
        'https://zfatt.oss-cn-beijing.aliyuncs.com/experiment/fydw/TaiDeng_C_BaseMap.png',
        function () {
          textureCube.mapping = THREE.EquirectangularReflectionMapping
          model.traverse(function (child) {
            if (child instanceof THREE.Mesh) {
              child.userData.draggable = true // 标记子模型为可拖拽
            }
            if (child.isMesh) {
              child.material.metalness = 0.7 // 金属度
              child.material.roughness = 0.5 // 表面粗糙度
              child.material.envMap = textureCube // 设置环境贴图
              child.material.envMapIntensity = 1 // 环境贴图影响程度
            }
          })

          const boundingBox = new THREE.Box3().setFromObject(model) // 计算模型的包围盒
          modelSize = boundingBox.getSize(new THREE.Vector3()).length()
          const fov = camera.fov * (Math.PI / 180) // 视角转换为弧度
          const cameraDistance = modelSize / (2 * Math.tan(fov / 2)) + 0.2
          const target = boundingBox.getCenter(new THREE.Vector3())

          camera.position.set(target.x, target.y, target.z + cameraDistance) // 设置相机位置
          camera.lookAt(target)

          if (glat.animations.length) {
            animations = glat.animations
            createAnimation() // 创建动画
          }

          if (newModel) {
            scene.add(newModel)
            controls.target.set(target.x, target.y, target.z)
            controls.update()
          } else {
            scene.add(model)
            animate()
            initControls(target.x, target.y, target.z) // 初始化控制器
          }
          loading.value = false
        }
      )
    },
    undefined,
    function (error) {
      console.error(error) // 加载失败时的错误处理
    }
  )
}

// 清理模型的资源
function disposeModel(model) {
  model.traverse(function (object) {
    if (object.isMesh) {
      object.geometry.dispose()
      if (object.material.map) {
        object.material.map.dispose() // 清理纹理资源
      }
    }
  })
  actionState.value = false
  splittingState.value = false
  combinationState.value = false
}

// 鼠标事件处理逻辑
const onMouseDown = (event) => {
  if (event.button !== 0) {
    return false
  }
  event.preventDefault()
  onMouseClick(event)
  isDragging = true
  previousMousePosition = {
    x: event.clientX,
    y: event.clientY
  }
}

const onMouseMove = (event) => {
  if (event.button !== 0) {
    return false
  }
  event.preventDefault()
  if (isDragging && selectedObject) {
    const deltaMove = {
      x: event.clientX - previousMousePosition.x,
      y: event.clientY - previousMousePosition.y
    }
    const cameraDirection = new THREE.Vector3()
    camera.getWorldDirection(cameraDirection)
    const movementDirection = new THREE.Vector3(deltaMove.x, -deltaMove.y, 0)
    movementDirection.applyQuaternion(camera.quaternion)
    let ratio =
      modelSize < 0.3 ? modelSize / 220 : modelSize / (modelSize * 300)
    selectedObject.position.add(movementDirection.multiplyScalar(ratio))
    previousMousePosition = {
      x: event.clientX,
      y: event.clientY
    }
  }
}

const onMouseUp = (event) => {
  event.preventDefault()
  if (event.button !== 0) {
    return false
  }
  event.target.style.cursor = 'default'
  isDragging = false
}

const onDoubleClick = () => {
  if (selectedObject) {
    emit('toggleModel', selectedObject) // 双击切换模型状态
  }
}

// 动画相关逻辑
let actionState = ref(false)
let splittingState = ref(false) // 拆解动画状态
let combinationState = ref(false) // 合并动画状态

function combination() {
  if (actionState.value && !combinationState.value) {
    combinationState.value = true
    action.paused = false
    action.time = 2 // 动画时间
    action.play()
    setTimeout(() => {
      actionState.value = false
      combinationState.value = false
    }, 1000)
  }
}

function splitting() {
  if (!actionState.value && !splittingState.value) {
    splittingState.value = true
    action.stop()
    action.time = 0.5 // 动画时间
    action.play()
    setTimeout(() => {
      action.paused = true
      actionState.value = true
      splittingState.value = false
    }, 1000)
  }
}

let mixer, action
function createAnimation() {
  mixer = new THREE.AnimationMixer(model)
  var animation = animations[0]
  action = mixer.clipAction(animation)
  action.loop = THREE.LoopOnce
  action.clampWhenFinished = true
}

// 重新设置视图
function setModelSize(type) {
  if (type === 'spread') {
    renderer.setSize(807, modelBox.value.clientHeight)
    camera.aspect = 807 / modelBox.value.clientHeight
  } else {
    renderer.setSize(1000, modelBox.value.clientHeight)
    camera.aspect = 1000 / modelBox.value.clientHeight
  }
  camera.updateProjectionMatrix()
}
</script>

<template>
  <!-- 模型容器 -->
  <div class="model" ref="modelBox"></div>
  <!-- 加载动画 -->
  <img
    v-if="loading"
    class="load"
    src="@/assets/experiment/modelLoad.gif"
    alt=""
  />
</template>

<style lang="scss" scoped>
.model {
  width: 100%;
  height: 100%;
}
.load {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
}
</style>
